/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.qp.basics.estivate.jdbc.dao;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.*;

/**
 * 本类提供了多种ResultSet和Map以及数组，以及JPA实体类，普通POJO的转换。 Staff staff = (Staff)
 * BasicRowProcessor.instance().toBean(rs, Staff.class); Staff staff = (Staff)
 * BasicRowProcessor.instance().toJPABean(rs, Staff.class);
 * 
 * Basic implementation of the <code>RowProcessor</code> interface.
 * 
 * <p>
 * This class is thread-safe.
 * </p>
 * 
 */
public class BasicRowProcessor {

	/**
	 * The default BeanProcessor instance to use if not supplied in the
	 * constructor.
	 */
	private static final BeanProcessor defaultConvert = new BeanProcessor();

	/**
	 * The Singleton instance of this class.
	 */
	private static final BasicRowProcessor instance = new BasicRowProcessor();

	/**
	 * Returns the Singleton instance of this class.
	 *
	 * @return The single instance of this class.
	 */
	public static BasicRowProcessor instance() {
		return instance;
	}

	/**
	 * Use this to process beans.
	 */
	private BeanProcessor convert = null;

	/**
	 * BasicRowProcessor constructor. Bean processing defaults to a
	 * BeanProcessor instance.
	 */
	public BasicRowProcessor() {
		this(defaultConvert);
	}

	/**
	 * BasicRowProcessor constructor.
	 * 
	 * @param convert
	 *            The BeanProcessor to use when converting columns to bean
	 *            properties.
	 * @since DbUtils 1.1
	 */
	public BasicRowProcessor(BeanProcessor convert) {
		super();
		this.convert = convert;
	}

	/**
	 * Convert a <code>ResultSet</code> row into an <code>Object[]</code>. This
	 * implementation copies column values into the array in the same order
	 * they're returned from the <code>ResultSet</code>. Array elements will be
	 * set to <code>null</code> if the column was SQL NULL. 这个方法是对应一行数据的。
	 * 首先需要保证现在rs已经next过了。
	 * 
	 * @see org.apache.commons.dbutils.RowProcessor#toArray(ResultSet)
	 */
	public Object[] toArray(ResultSet rs) throws SQLException {
		ResultSetMetaData meta = rs.getMetaData();
		int cols = meta.getColumnCount();
		Object[] result = new Object[cols];

		for (int i = 0; i < cols; i++) {
			result[i] = rs.getObject(i + 1);
		}

		return result;
	}

	public List toObjectArrayList(ResultSet rs) throws SQLException {
		List rsList = new ArrayList();
		while (rs.next()) {
			rsList.add(toArray(rs));
		}

		return rsList;
	}

	/**
	 * Convert a <code>ResultSet</code> row into a JavaBean. This implementation
	 * delegates to a BeanProcessor instance.
	 * 
	 * @see org.apache.commons.dbutils.RowProcessor#toBean(ResultSet,
	 *      Class)
	 * @see org.apache.commons.dbutils.BeanProcessor#toBean(ResultSet,
	 *      Class)
	 */
	public Object toBean(ResultSet rs, Class type) throws SQLException {
		return this.convert.toBean(rs, type);
	}

	/**
	 * Convert a <code>ResultSet</code> into a <code>List</code> of JavaBeans.
	 * This implementation delegates to a BeanProcessor instance.
	 * 
	 * @see org.apache.commons.dbutils.RowProcessor#toBeanList(ResultSet,
	 *      Class)
	 * @see org.apache.commons.dbutils.BeanProcessor#toBeanList(ResultSet,
	 *      Class)
	 */
	public List toBeanList(ResultSet rs, Class type) throws SQLException {
		return this.convert.toBeanList(rs, type);
	}

	/**
	 * Convert a <code>ResultSet</code> row into a <code>Map</code>. This
	 * implementation returns a <code>Map</code> with case insensitive column
	 * names as keys. Calls to <code>map.get("COL")</code> and
	 * <code>map.get("col")</code> return the same value.
	 * 
	 * @see org.apache.commons.dbutils.RowProcessor#toMap(ResultSet)
	 */
	public Map toMap(ResultSet rs) throws SQLException {
		Map result = new CaseInsensitiveHashMap();
		ResultSetMetaData rsmd = rs.getMetaData();
		int cols = rsmd.getColumnCount();

		for (int i = 1; i <= cols; i++) {
			result.put(rsmd.getColumnLabel(i), rs.getObject(i));
		}

		return result;
	}

	public List toMapList(ResultSet rs) throws SQLException {
		List rsList = new ArrayList();
		while (rs.next()) {
			rsList.add(toMap(rs));
		}

		return rsList;
	}

	/**
	 * A Map that converts all keys to lowercase Strings for case insensitive
	 * lookups. This is needed for the toMap() implementation because databases
	 * don't consistenly handle the casing of column names.
	 */
	private static class CaseInsensitiveHashMap extends HashMap {

		/**
		 * @see Map#containsKey(Object)
		 */
		public boolean containsKey(Object key) {
			return super.containsKey(key.toString().toLowerCase());
		}

		/**
		 * @see Map#get(Object)
		 */
		public Object get(Object key) {
			return super.get(key.toString().toLowerCase());
		}

		/**
		 * @see Map#put(Object, Object)
		 */
		public Object put(Object key, Object value) {
			return super.put(key.toString().toLowerCase(), value);
		}

		/**
		 * @see Map#putAll(Map)
		 */
		public void putAll(Map m) {
			Iterator iter = m.keySet().iterator();
			while (iter.hasNext()) {
				Object key = iter.next();
				Object value = m.get(key);
				this.put(key, value);
			}
		}

		/**
		 * @see Map#remove(Object)
		 */
		public Object remove(Object key) {
			return super.remove(key.toString().toLowerCase());
		}
	}

	public Map toJPAMap(ResultSet rs) throws SQLException {
		throw new RuntimeException("uninplemented");
	}

}
